package org.jboss.arquillian.container.appengine.embedded.hack; import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.security.Permission; import java.util.HashMap; import java.util.Map; import java.util.PropertyPermission; import com.google.appengine.tools.development.AppContext; import com.google.appengine.tools.development.DevAppServer; import com.google.appengine.tools.development.DevAppServerClassLoaderExposed; import com.google.appengine.tools.development.DevAppServerFactory; /** * DevAppServerFactory * * @author Google GAE team */ public class DevAppServerFactoryHack { private static final Class[] DEV_APPSERVER_CTOR_ARG_TYPES = {File.class, File.class, File.class, File.class, String.class, Integer.TYPE, Boolean.TYPE, Map.class}; public static DevAppServer createDevAppServer(File appLocation, String bindAddress, int bindHttpPort) { return createDevAppServer(new Object[]{appLocation, null, null, null, bindAddress, bindHttpPort, true, new HashMap()}); } private static DevAppServer createDevAppServer(Object[] ctorArgs) { ClassLoader loader = DevAppServerClassLoaderExposed.newClassLoader(DevAppServerFactory.class.getClassLoader()); DevAppServer devAppServer; try { Class<?> devAppServerClass = Class.forName("com.google.appengine.tools.development.DevAppServerImpl", true, loader); Constructor<?> cons = devAppServerClass.getConstructor(DEV_APPSERVER_CTOR_ARG_TYPES); cons.setAccessible(true); devAppServer = (DevAppServer) cons.newInstance(ctorArgs); } catch (Exception e) { Throwable t = e; if (e instanceof InvocationTargetException) { t = e.getCause(); } throw new RuntimeException("Unable to create a DevAppServer", t); } System.setSecurityManager(new CustomSecurityManager(devAppServer)); return devAppServer; } private static class CustomSecurityManager extends SecurityManager { private static final RuntimePermission PERMISSION_MODIFY_THREAD_GROUP = new RuntimePermission("modifyThreadGroup"); private static final RuntimePermission PERMISSION_MODIFY_THREAD = new RuntimePermission("modifyThread"); private static final String KEYCHAIN_JNILIB = "/libkeychain.jnilib"; private static final Object PERMISSION_LOCK = new Object(); private final DevAppServer devAppServer; public CustomSecurityManager(DevAppServer devAppServer) { this.devAppServer = devAppServer; } private synchronized boolean appHasPermission(Permission perm) { synchronized (PERMISSION_LOCK) { AppContext context = this.devAppServer.getAppContext(); if ((context.getUserPermissions().implies(perm)) || (context.getApplicationPermissions().implies(perm))) { return true; } } return ("read".equals(perm.getActions())) && (perm.getName().endsWith(KEYCHAIN_JNILIB)); } public void checkPermission(Permission perm) { if (perm instanceof PropertyPermission) { return; } if (isDevAppServerThread()) { if (appHasPermission(perm)) { return; } super.checkPermission(perm); } } public void checkPermission(Permission perm, Object context) { if (isDevAppServerThread()) { if (appHasPermission(perm)) { return; } super.checkPermission(perm, context); } } public void checkAccess(ThreadGroup g) { if (g == null) { throw new NullPointerException("thread group can't be null"); } checkPermission(PERMISSION_MODIFY_THREAD_GROUP); } public void checkAccess(Thread t) { if (t == null) { throw new NullPointerException("thread can't be null"); } checkPermission(PERMISSION_MODIFY_THREAD); } public boolean isDevAppServerThread() { return Boolean.getBoolean("devappserver-thread-" + Thread.currentThread().getName()); } } }